//============================================================================
// Name        : TP4_DAEMON_ESTABILIDAD.cpp
// Author      : 
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include "IncludesServidor.h"
#include <ostream>
#include <sstream>
#include <istream>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <list>
#include <iostream>
#include <sstream>

#define TAM_VECTOR_PARTIDAS 100

using namespace std;

SharedMemory *shm  = NULL;							// Puntero al objeto que administra los accesos a la shm con el torneo.
Semaforo* semShm   = NULL;							// Puntero al semaforo que sincroniza el acceso a la shm con el torneo.
SharedMemory *shmPartida  = NULL;
Semaforo* semShmPartida  = NULL;
char semShmNamePartida[50] = {CONST_CERO};
pid_t pidPartida;
int shmid;
t_vector_daemon *buffer;
long int partidas[TAM_VECTOR_PARTIDAS];
long int cnPartidas = CONST_CERO;
key_t llave;

/* Se fija si un pid dado es zombie, es para las partidas porque no mueren porque son hijos */
bool isDead(pid_t pidPartida);
/* Maneja la señal enviada por el torneo para terminar en caso de que termine bien */
void signalHandler(int signal);
/* Conecta a la memoria compartida con el servidor */
bool conectarShm();
/* Leo el vector de partidas */
void leerVectorPartidas();
/* Libera la memoria compartida */
void cerrarShm();
/* Se encarga de terminar todas las partidas y liberar la memoria correspondiente */
void terminarPorTorneoMuerto();

/*
 * Este proceso esta a cargo de la estabilidad del sistema en general, verificando si muere
 * alguno de los procesos, para avisar a los demas y cerrar todos los recursos que pueda haber
 * dejado tomado.
 */
int main() {
	/* Me attacho a las señales de finalización que me puede enviar el torneo. */
	signal(SENIAL_FIN_TORNEO,signalHandler);

	/* Creo el nombre del semaforo para la shm */
	char semShmName[50] = {0};
	ostringstream ss;
	ss << "sem" << getpid();
	strcpy(semShmName, ss.str().c_str());

	/* Tomo el pid del padre, que es el torneo quien lo inicia */
	long int pidTorneo = getppid();

    /* Recibo la key para la shared memory */
	llave = (key_t)getpid();

	/* instancio la shm de lectura y escritura y el semaforo para su uso exclusivo */
	semShm = new Semaforo(1,semShmName);

    /* Conecto a la memoria compartida */
    if(!conectarShm()){
        cout << "[ERROR] - No se pudo abrir la memoria compartida del server desde el daemon, se cierra el daemon. " << endl;
        exit(CONST_UNO);
    }

    /* Me pongo a iterar mientras dure el torneo para verificar la estabilidad */
    while(true){
        /* Leo la memoria compartida actualizada */
        leerVectorPartidas();
        semShm->V();

    	/* Si el torneo no recibe el kill es porque se cayo, entonces procedo a bajar todo el torneo */
    	if(kill(pidTorneo, 0) == -1){
    		terminarPorTorneoMuerto();
    		break;

    	}else{
    		/* Recorro la lista de partidas verificando que no halla muerto ninguna. */
    		for (int x=0; x < cnPartidas; x++){
    			/* tomo el pid de cada partida */
    			pidPartida = partidas[x];

    			if(isDead(pidPartida)){
    				kill(pidTorneo, SENIAL_FIN_DAEMON);
    				break;
    			}
    		}
    	}

    	/* Duermo un rato para no matar al CPU */
    	usleep(100000);
    }

	/* Desconecto la memoria compartida  y su semaforo */
	cerrarShm();
	semShm->Liberar();
	semShm->Eliminar();

	/* Logueo que termino todo */
	cout << "[FIN] - Termina el torneo por algun kill a maneta. " << endl;

	/* Salgo */
	exit(0);
}

/* Se fija si un pid dado es zombie, es para las partidas porque no mueren porque son hijos */
bool isDead(pid_t pidPartida){
	/* 									Me fijo si es zombie 								*/
	/* Traigo el listado de procesos zombies y filtrado por el pid de la partida */
	char cadena[1000] = {0};
	ostringstream ss;
	ss << "ps -A -ostat,pid | grep -e '[zZ]'| awk '{ print $2 }' | grep " << pidPartida;
	FILE *f = popen(ss.str().c_str(), "r");
	/* Si trajo algo es porque esta zombie, asi que devuelvo true */
	while(fgets(cadena, 1000, f)){
		wait(NULL);
		return CONST_TRUE;
	}
	wait(NULL);

	/* 			Si le tiro un kill y no acepta es porque ni siquiera es zombie, murio 		*/
	if(kill(pidPartida,0) == CONST_MENOS_UNO)
		return CONST_TRUE;

	return CONST_FALSE;
}

/* Handler que toma las señal de finalización del torneo para terminar. */
void signalHandler(int signal){
	//TODO: Manejar señales del DAEMON y de su propio thread.
	switch(signal){
		case SENIAL_FIN_TORNEO:
			/* Si tengo tomada alguna memoria o semaforo de partida la libero tambien */
			if(shmPartida != NULL)
				shmPartida->cerrarShm();

			if(semShmPartida != NULL){
				semShmPartida->Liberar();
				semShmPartida->Eliminar();
			}

			break;
	}

	exit(0);
}

/* Conecta a la memoria compartida con el servidor */
bool conectarShm(){
	if ((shmid = shmget(llave, 100, IPC_CREAT | 0666)) < CONST_CERO){
		return CONST_FALSE;
	}
	if ((buffer = (t_vector_daemon *)shmat(shmid, NULL, CONST_CERO)) == NULL) {
		return CONST_FALSE;
	}

	return CONST_TRUE;
}

/* Leo el vector de partidas */
void leerVectorPartidas(){
	/* Copio el vector en una estructura temporal */
	t_vector_daemon vectorTmp;
    memcpy(&vectorTmp, buffer, sizeof(t_vector_daemon));

	/* Veo cuantas partidas hay */
	for(int i=0; i<TAM_VECTOR_PARTIDAS; i++){
		if(vectorTmp.partidas[i] != 0){
			partidas[cnPartidas] = vectorTmp.partidas[i];
			cnPartidas++;
		}
	}
}

/* Libera la memoria compartida */
void cerrarShm(){
    if(shmdt(buffer) == -1){
    	exit(1);
    }

    shmctl(shmid, IPC_RMID, NULL);
}

/* Se encarga de terminar todas las partidas y liberar la memoria correspondiente */
void terminarPorTorneoMuerto(){
	/* Logue por sysout */
	cout << "[ERROR] - Se cayo el torneo, se procede a finalizar las partidas y liberar las memorias compartidas. " << endl;

	/* Recorro la lista de partidas verificando que no halla muerto ninguna. */
	for (int x=0; x < cnPartidas; x++){
		/* Tomo el pid de la partida en proceso de muerte */
		pidPartida = partidas[x];

		/* Envio la señal para que muera */
		kill(pidPartida, SENIAL_FIN_DAEMON);

		/* Creo el nombre del semaforo para la shm */
		semShmNamePartida[0] = 0;
		ostringstream ss2;
		ss2 << "sem" << pidPartida;
		strcpy(semShmNamePartida , ss2.str().c_str());

		/* Punteros locales para los semaforos y shm a cerrar de cada partida */
		shmPartida 		 = new SharedMemory((key_t)pidPartida);
		semShmPartida	 = new Semaforo(1, semShmNamePartida);

		/* Desconecto la memoria compartida  y su semaforo */
		shmPartida->conectarShm();
		shmPartida->cerrarShm();
		semShmPartida->Liberar();
		semShmPartida->Eliminar();
	}
}
